use super::app::AppConfig;
use flexi_logger::{Cleanup, Criterion, Duplicate, FileSpec, Logger, Naming, WriteMode};

pub struct Logging;

impl Logging {
    pub async fn init(config: &AppConfig) {
        match Logger::try_with_str(config.log_level.as_str()) {
            Ok(logger) => {
                match logger
                    .log_to_file(
                        FileSpec::default()
                            .directory("logs")
                            .basename("app")
                            .suffix("log"),
                    )
                    .rotate(
                        Criterion::Size(10 * 1024 * 1024), // 10 MB
                        Naming::Numbers,
                        Cleanup::KeepLogFiles(300),
                    )
                    .write_mode(WriteMode::Direct)
                    .format_for_files(detailed_format)
                    .format_for_stdout(console_format)
                    .duplicate_to_stdout(Duplicate::All)
                    .start()
                {
                    Ok(_) => log::info!("Logger initialized, log level: {}", log::max_level()),
                    Err(e) => panic!("Failed to start logger: {}", e),
                }
            }
            Err(e) => panic!("Failed to initialize logger: {}", e),
        }
    }
}

fn detailed_format(
    w: &mut dyn std::io::Write,
    now: &mut flexi_logger::DeferredNow,
    record: &log::Record,
) -> std::io::Result<()> {
    write!(
        w,
        "[{}] {:<5} [{:<55}] : {}",
        now.now().format("%Y-%m-%d %H:%M:%S%.6f %:z"),
        record.level(),
        format!(
            "{}:{}",
            record.module_path().unwrap_or("<unnamed>"),
            record.line().unwrap_or(0)
        ),
        &record.args()
    )
}

fn console_format(
    w: &mut dyn std::io::Write,
    now: &mut flexi_logger::DeferredNow,
    record: &log::Record,
) -> std::io::Result<()> {
    let level_str = match record.level() {
        log::Level::Error => "\x1b[31mERROR\x1b[0m",
        log::Level::Warn => "\x1b[33mWARN\x1b[0m",
        log::Level::Info => "\x1b[32mINFO\x1b[0m",
        log::Level::Debug => "\x1b[34mDEBUG\x1b[0m",
        log::Level::Trace => "\x1b[36mTRACE\x1b[0m",
    };

    write!(
        w,
        "[{}] {:<20} [{:<55}] {}",
        now.now().format("%Y-%m-%d %H:%M:%S"),
        level_str,
        format!(
            "{}:{}",
            record.module_path().unwrap_or("<unnamed>"),
            record.line().unwrap_or(0)
        ),
        record.args()
    )
}
